package top.suven.base.http.exception;


import com.alibaba.dubbo.rpc.RpcException;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.servlet.NoHandlerFoundException;
import top.suven.base.http.data.vo.ResponseResultVO;
import top.suven.base.http.enums.IMsgEnumType;
import top.suven.base.http.enums.SysMsgEnumType;
import top.suven.base.http.message.ParamMessage;
import top.suven.base.http.message.RequestMessage;
import top.suven.base.util.constants.Env;
import top.suven.base.util.utilize.JsonFormatTool;
import top.suven.base.util.utilize.JsonUtils;

import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author vincentdeng
 *
 */
@ControllerAdvice
public class GlobalExceptionHandler {

	private static Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

//    /**
//     * 应用到所有@RequestMapping注解方法，在其执行之前初始化数据绑定器
//     * @param binder
//     */
//    @InitBinder
//    public void initBinder(WebDataBinder binder) {}
	@ResponseBody
	@ResponseStatus(HttpStatus.NOT_FOUND)
	@ExceptionHandler({NoHandlerFoundException.class })
	protected ResponseResultVO handleNoHandlerFoundException(NoHandlerFoundException e, HttpServletResponse response) {

		loggerError(e);
		return this.write(SysMsgEnumType.SYS_INVALID_REQUEST, response);
	}

	@ResponseStatus(HttpStatus.OK)
	@ExceptionHandler({ SystemRuntimeException.class })
	@ResponseBody
	protected ResponseEntity handleRuntimeException(SystemRuntimeException e, HttpServletResponse response) {
		loggerError(e);
		if(e.getError()==null)
			return new ResponseEntity(this.write(e, response), HttpStatus.OK);
		else
			return new ResponseEntity(this.write(e.getError(), response), HttpStatus.OK);
	}

	@ResponseStatus(HttpStatus.OK)
	@ExceptionHandler({ BusinessLogicException.class })
	@ResponseBody
	protected ResponseResultVO handleBusinessException(BusinessLogicException e, HttpServletResponse response) {

		loggerError(e);
		if(e.getError() == null)
			return this.write(SysMsgEnumType.SYS_UNKOWNN_FAIL, response);
		else
			return this.write(e.getError(), response);
	}

	@ResponseBody
	@ResponseStatus(HttpStatus.BAD_REQUEST)
	@ExceptionHandler({ IllegalArgumentException.class })
	protected ResponseResultVO handleIllegalArgumentException(IllegalArgumentException e, HttpServletResponse response) {

		loggerError(e);
		return this.write(SysMsgEnumType.SYS_INVALID_REQUEST, response);
	}

	@ResponseBody
	@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
	@ExceptionHandler({ Exception.class })
	protected ResponseEntity handleException(Exception e, HttpServletResponse response) {
		if(e.getCause() instanceof  SystemRuntimeException){
			return handleRuntimeException((SystemRuntimeException)e.getCause(),response);
		}
		loggerError(e);
		return new ResponseEntity(this.write(SysMsgEnumType.SYS_UNKOWNN_FAIL, response),HttpStatus.INTERNAL_SERVER_ERROR);
	}

	@ResponseStatus(HttpStatus.OK)
	@ExceptionHandler({ RpcException.class })
	@ResponseBody
	protected ResponseEntity handleException(RpcException e, HttpServletResponse response) {
		if(StringUtils.isBlank(e.getMessage())||e.getMessage().indexOf("com.alibaba.csp.sentinel.slots.block.flow.FlowException")==-1){
			return handleException((Exception)e,response);
		}
		SystemRuntimeException systemRuntimeException = new SystemRuntimeException(SysMsgEnumType.SYS_VISITS_WAIT);
		return new ResponseEntity(this.write(systemRuntimeException, response), HttpStatus.OK);
	}

	@ResponseStatus(HttpStatus.OK)
	@ExceptionHandler({ BindException.class })
	@ResponseBody
	protected ResponseResultVO handleException(BindException e, HttpServletResponse response) {
		List<ObjectError> objectErrorList = e.getAllErrors();
		String message = String.join("，",objectErrorList.stream().map(a->a.getDefaultMessage()).collect(Collectors.toList()));
		SystemRuntimeException systemRuntimeException = new SystemRuntimeException(SysMsgEnumType.SYS_PARAM_ERROR, new String[]{message});
		return this.write(systemRuntimeException, response);
	}


	private  ResponseEntity<ResponseResultVO> write(Object enumType ,HttpServletResponse response,HttpStatus status){
		response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
		response.setHeader("Accept",MediaType.APPLICATION_JSON_VALUE);
		ResponseResultVO vo = this.write(enumType, response);
		ResponseEntity.BodyBuilder builder = ResponseEntity.ok()
//				.header("Accept",MediaType.APPLICATION_JSON_VALUE)
				.contentType(MediaType.APPLICATION_JSON_UTF8);
//		.allow(HttpMethod.GET,HttpMethod.POST)
		return builder.body(vo);

	}

	private ResponseResultVO write(Object message, HttpServletResponse response) {
		response.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
		response.setHeader("Accept",MediaType.APPLICATION_JSON_VALUE);
		ResponseResultVO result = new ResponseResultVO();
		if(message instanceof  SystemRuntimeException){
			SystemRuntimeException exception  = (SystemRuntimeException)message;
			result.setCode(exception.getErrorCode());
			result.setMsg(exception.getErrorMessage());
			if(exception.getResponse() != null) {
				result.setResponse(exception.getResponse());
			}
		}
		if(message instanceof BusinessLogicException){
			BusinessLogicException logic  = (BusinessLogicException)message;
			result.setCode(logic.getErrorCode());
			result.setMsg(logic.getErrorMessage());
		}
		if(message instanceof IMsgEnumType){
			IMsgEnumType msg  = (IMsgEnumType)message;
			result.setCode(msg.getCode());
			result.setMsg(msg.getMsg());
		}
		printErrorLogForRequestMessage(result.getCode(), result.getMsg());
		if(!Env.isProd()){
			logger.warn(JsonFormatTool.formatJson(JsonUtils.toJsonString(result)));
		}
		return result;
	}


	private void loggerError(Exception e){
		String errorMessage = e.getMessage();
		if(e instanceof SystemRuntimeException){
			errorMessage =((SystemRuntimeException) e).getErrorMessage();
		}
		StringBuffer msg = new StringBuffer().append(e.getClass().getSimpleName()).append(":").append(errorMessage);
		StackTraceElement[] stackTrace = e.getStackTrace();
		for (StackTraceElement stackTraceElement : stackTrace) {
			msg.append("\n\t at " + stackTraceElement.toString());
		}
		if(e instanceof SystemRuntimeException){
			logger.info(msg.toString());
			return;
		}
		logger.warn(msg.toString());
	}
	private void printErrorLogForRequestMessage(int code, String  msg) {
		RequestMessage message = ParamMessage.getRequestMessage();
		String  json = ParamMessage.getJsonParseString();
		long netTime = ParamMessage.getRequestRemote().getNetTime();
		long exeTime = System.currentTimeMillis() - message.getTimes();
		logger.warn("type=Exception, url=[{}], code=[{}],  msg=[{}], ip=[{}],Request_BaseParam = [{}],Request_AttributeParam = [{}], responseEndTime =[{}] ,  netEndTime =[{}]",
				message.getUri(), code, msg, message.getIp(),
				message.toString(),json,exeTime,netTime);
	}

}
